package com.vcc.web.controller;


import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.secure.totp.SaTotpUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import cn.hutool.json.JSONUtil;
import com.mybatisflex.core.paginate.Page;
import com.vcc.core.annotations.*;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.exception.SignInException;
import com.vcc.core.model.StatusEnum;
import com.vcc.core.util.*;
import com.vcc.data.entity.*;
import com.vcc.service.boss.request.user.UserRegisterDto;
import com.vcc.service.client.dto.user.*;
import com.vcc.service.client.vo.kyc.KycCertInfoVo;
import com.vcc.service.client.vo.user.UserVo;
import com.vcc.service.service.*;
import com.vcc.web.core.component.SmsComponent;
import com.vcc.web.core.config.AccountConfig;
import com.vcc.web.core.dto.login.BindGoogleDto;
import com.vcc.web.core.dto.login.GoogleLoginDto;
import com.vcc.web.core.dto.user.CheckUserSafeDto;
import com.vcc.web.core.util.UserUtil;
import com.vcc.web.core.vo.login.EncryptVo;
import com.vcc.web.core.vo.login.LoginVo;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.security.KeyPair;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@RestController
@RequestMapping("user")
public class UserController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    @Autowired
    private UserService userService;
    @Autowired
    private AccountConfig accountConfig;
    @Autowired
    private CurrencyService currencyService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private WalletService walletService;
    @Resource
    private CardTypeService cardService;
    @Autowired
    private UserCardWalletService userCardWalletService;
    @Autowired
    private UserCardService userCardService;
    @Autowired
    private WalletDetailService walletDetailService;
    @Autowired
    private ComptrollerService comptrollerService;
    @Autowired
    private SmsComponent smsComponent;
    //    @Autowired
//    private RedisCustomizeUtils RedisCustomizeUtils;
    @Autowired
    private ClientPermissionService clientPermissionService;
    @Autowired
    private ClientParamService clientParamService;
    @Autowired
    private KycCertInfoService kycCertInfoService;
    @Autowired
    private BossUserService bossUserService;
    @Autowired
    private ClientRolePermissionService clientRolePermissionService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private EnterpriseCertInfoService enterpriseCertInfoService;
    @Resource
    private WhiteLabelService whiteLabelService;

    @Value("${spring.profiles.active}")
    private String activeProfile;
    @Autowired
    private CardGroupService cardGroupService;
    @Autowired
    private ShareAccountService shareAccountService;

    public static void main(String[] args) {
        KeyPair pair = SecureUtil.generateKeyPair("RSA", 2048);
        String privateKey = Base64.encode(pair.getPrivate().getEncoded());
        String publicKey = Base64.encode(pair.getPublic().getEncoded());
        System.out.println("Private Key: " + privateKey);
        System.out.println("Public Key: " + publicKey);
    }

    @RestPack
    @GetMapping("get")
    @SaCheckLogin
    public UserVo get() {
        User user = userService.getById(UserUtil.getId());
        if (StringUtils.isBlank(user.getInvitationCode())) {
            String s;
            do {
                s = RandomUtil.randomString(8);
            } while (userService.existsByInvitationCode(s));
            user.setInvitationCode(s);
            userService.setInvitationCode(user.getId(), s);
            UserUtil.set(user, UserUtil.getRole());
        }
        if (user.getStatus() != 0) {
            throw new SignInException("当前用户状态异常，请联系管理员");
        }
        if (user.getParentId() != null && user.getParentId() != 0) {
            Integer parentStatus = userService.getStatusById(user.getParentId());
            if (parentStatus == null || !parentStatus.equals(0)) {
                userService.updateStatusById(user.getId(), parentStatus);
                logger.info("用户ID: {}, 父级用户ID: {}, 状态: {},账号状态异常，提出登录", user.getId(), user.getParentId(), parentStatus);
                throw new SignInException("主账户状态异常，请联系管理员");
            }
        }
        Role role = roleService.getRole(user.getId());
        List<Wallet> wallets = walletService.listByUserId(user.getId());
        List<String> permissions = clientPermissionService.listByUserId(user.getId());
        Long shareAccountCount = shareAccountService.countByUserId(user.getId());
        Integer kycStatus = null;
        Integer kycType = null;
        String clientUserFlag = clientParamService.getValueByCodeAndUserIdOrDefaultValue("HAS_PERSONAL", user.getId(), "true");
        if (clientUserFlag.equals("false")) {
            kycStatus = 0;
        } else {
            if (kycCertInfoService.existsByUserId(user.getId())) {
                KycCertInfoVo kycCertInfoVo = kycCertInfoService.getByUserId(user.getId());
                kycStatus = kycCertInfoVo.getStatus();
                kycType = 0;
            }
            if (enterpriseCertInfoService.existsByUserId(user.getId())) {
                EnterpriseCertInfo enterpriseCertInfo = enterpriseCertInfoService.getByUserId(user.getId());
                kycStatus = (enterpriseCertInfo.getStep() != 6 && enterpriseCertInfo.getStep() != 7) ? null : enterpriseCertInfo.getStatus();
                kycType = 1;
            }
        }
        String clientWebPhone = "";
        if (user.getBeLongToDealer() != null && user.getBeLongToDealer() != 0) {
            clientWebPhone = bossUserService.getAccountById(user.getBeLongToDealer());
        } else {
            clientWebPhone = clientParamService.getValueByCodeAndUserIdOrDefaultValue("CLIENT_WEB_PHONE", user.getId(), "");
        }
        //特殊检查
        if (!cardGroupService.existsByNameAndUserId(user.getId(),"默认分组")) {
            CardGroup cardGroup = cardGroupService.initDefaultGroup(user.getId());
            userCardService.defaultSetGroupId(user.getId(), cardGroup.getId());
        }
        return new UserVo(user, role, wallets, permissions, kycStatus, kycType, clientWebPhone,shareAccountCount);
    }

    @GetMapping("getAccountEncryptKey")
    @RestPack
    public EncryptVo getAccountEncryptKey(@CurrentClientIp String clientIp) {
//        String limitKey = "client:encrypt:count:" + clientIp.replace(".", "");
//        Long increase = RedisCustomizeUtils.increase(limitKey, 1L, 60L);
//        if (increase > 5) {
//            throw new ProgramException("当前IP请求次数过多，请稍后再试");
//        }
        String uuid = IdUtil.randomUUID();
        KeyPair keyPair = SecureUtil.generateKeyPair("RSA", 2048);
        String publicKey = Base64.encode(keyPair.getPublic().getEncoded());
        String privateKey = Base64.encode(keyPair.getPrivate().getEncoded());
        RedisCustomizeUtils.set("client:encrypt:privateKey:" + uuid, privateKey, 5L, TimeUnit.MINUTES);
        return EncryptVo.builder()
                .encryptKey(publicKey)
                .verifyCode(uuid)
                .build();
    }

    @RestPack
    @PostMapping("login")
    public LoginVo login(@CurrentClientIp String clientIp, @RequestBody @Validated UserLoginDto userLoginDto, HttpServletRequest request, HttpServletResponse httpServletResponse) {
        if (RegexUtil.checkStringIsNull(userLoginDto.getAccount())) {
            throw new ProgramException("账号不能为空");
        }
        if (RegexUtil.checkStringIsNull(userLoginDto.getPassword())) {
            throw new ProgramException("密码不能为空");
        }
        if (RegexUtil.checkStringIsNull(userLoginDto.getVerifyCode())) {
            throw new ProgramException("验证码不能为空");
        }
        if (!RedisCustomizeUtils.hasKey("client:encrypt:privateKey:" + userLoginDto.getVerifyCode())) {
            throw new ProgramException("当前链接已使用或已过期");
        }
        String privateKey = RedisCustomizeUtils.get("client:encrypt:privateKey:" + userLoginDto.getVerifyCode());
        RedisCustomizeUtils.removeKey("client:encrypt:privateKey:" + userLoginDto.getVerifyCode());
        RSA rsa = SecureUtil.rsa(Base64.decode(privateKey), null);
        String account = rsa.decryptStr(userLoginDto.getAccount(), KeyType.PrivateKey);
        String password = rsa.decryptStr(userLoginDto.getPassword(), KeyType.PrivateKey);
        User user = userService.login(account);
        String host = request.getHeader("source");

        if (activeProfile.equals("prod") && !host.equals("www.allwincard.com")) {
            if (!whiteLabelService.existsByDomain(host)) {
                throw new ProgramException("当前域名未绑定，无法使用");
            }
            WhiteLabel whiteLabel = whiteLabelService.getByDomain(host);
            if (whiteLabel.getStatus() == null || whiteLabel.getStatus() != 0) {
                throw new ProgramException("当前域名未绑定，无法使用");
            }
            Long bossUserParentId = bossUserService.getParentIdById(user.getBeLongToDealer());
            if (!Objects.equals(whiteLabel.getBossUserId(), user.getBeLongToDealer()) && !whiteLabel.getBossUserId().equals(bossUserParentId)) {
                throw new ProgramException("当前用户所属域名与访问域名不匹配，无法使用");
            }
        }

        if (!EncryptUtil.encryptValue(password, user.getSalt()).equals(user.getPassword())) {
            Long increase = RedisCustomizeUtils.increase("login:error:count:" + user.getId(), 1L, 60L);
            if (increase >= 5) {
                userService.updateStatusById(user.getId(), 2);
                comptrollerService.block(clientIp, user.getId(), 0);
                throw new ProgramException("登录失败次数过多，账户已被锁定");
            }
            throw new ProgramException("账号或密码不正确");
        }
        if (user.getStatus().equals(4)) {
            smsComponent.sendActiveEmail(user.getEmail(), user.getId());
            throw new ProgramException(StatusEnum.UN_ACTIVE);
        }
        if (!user.getStatus().equals(0)) {
            throw new ProgramException("您的账户处于异常状态，无法登录");
        }
        String key = IdUtil.randomUUID();
        RedisCustomizeUtils.set(key, user.getId(), 5L, TimeUnit.MINUTES);
        String googleSecret = "";
        String googleQrCode = "";
        if (!user.getHasBindGoogle()) {
            googleSecret = SaTotpUtil.generateSecretKey();
            googleQrCode = QrCodeUtil.generateAsBase64(SaTotpUtil.generateGoogleSecretKey(user.getAccount(), googleSecret), new QrConfig(300, 300), "png");
            RedisCustomizeUtils.set("google:" + key, googleSecret, 5L, TimeUnit.MINUTES);
        }
        return LoginVo.builder()
                .verifyCode(key)
                .hasBindGoogle(user.getHasBindGoogle())
                .googleQrCode(googleQrCode)
                .googleSecret(googleSecret)
                .build();


    }

    @PostMapping("bindGoogle")
    @RestPack
    public Boolean bindGoogle(@RequestBody @Validated BindGoogleDto bindGoogleDto) {
        if (!RedisCustomizeUtils.hasKey("google:" + bindGoogleDto.getVerifyCode())) {
            throw new ProgramException("当前链接已使用或已过期");
        }
        if (!RedisCustomizeUtils.hasKey(bindGoogleDto.getVerifyCode())) {
            throw new ProgramException("当前链接已使用或已过期");
        }
        String googleSecret = RedisCustomizeUtils.get("google:" + bindGoogleDto.getVerifyCode());
        RedisCustomizeUtils.removeKey("google:" + bindGoogleDto.getVerifyCode());
        Long userId = Long.valueOf(RedisCustomizeUtils.get(bindGoogleDto.getVerifyCode()));
        RedisCustomizeUtils.removeKey(bindGoogleDto.getVerifyCode());
        User user = userService.getById(userId);
        if (user.getHasBindGoogle()) {
            throw new ProgramException("当前用户已绑定谷歌验证码");
        }
        if (!SaTotpUtil.validateTOTP(googleSecret, bindGoogleDto.getCode(), 1)) {
            throw new ProgramException("谷歌验证码不正确");
        }
        userService.bindGoogle(userId, SaSecureUtil.aesEncrypt(user.getSalt(), googleSecret));
        return true;
    }

    @PostMapping("googleLogin")
    @RestPack
    public SaTokenInfo googleLogin(@CurrentClientIp String clientIp, @RequestBody @Validated GoogleLoginDto googleLoginDto) {
        if (!RedisCustomizeUtils.hasKey(googleLoginDto.getVerifyCode())) {
            throw new ProgramException("当前链接已使用或已过期");
        }
        Long userId = Long.valueOf(RedisCustomizeUtils.get(googleLoginDto.getVerifyCode()));
        User user = userService.getById(userId);
        if (!user.getHasBindGoogle()) {
            throw new ProgramException("当前用户尚未绑定谷歌验证码，无法登录");
        }
        if (!SaTotpUtil.validateTOTP(SaSecureUtil.aesDecrypt(user.getSalt(), user.getGoogleSecret()), googleLoginDto.getCode(), 1) && activeProfile.equals("prod")) {
            Long increase = RedisCustomizeUtils.increase("login:error:count:" + user.getId(), 1L, 60L);
            if (increase >= 5) {
                userService.updateStatusById(user.getId(), 2);
                comptrollerService.block(clientIp, user.getId(), 0);
                throw new ProgramException("登录失败次数过多，账户已被锁定");
            }
            throw new ProgramException("谷歌验证码不正确");
        }
        RedisCustomizeUtils.removeKey(googleLoginDto.getVerifyCode());
        comptrollerService.login(clientIp, user.getId(), 0);
        //登录成功后，生成token
        UserUtil.login(user.getId(), null);
        UserUtil.set(user, roleService.getRole(user.getId()));
        RedisCustomizeUtils.removeKey("user_client_permission:" + user.getId());
        if (!cardGroupService.existsByNameAndUserId(user.getId(),"默认分组")) {
            CardGroup cardGroup = cardGroupService.initDefaultGroup(user.getId());
            userCardService.defaultSetGroupId(user.getId(), cardGroup.getId());
        }
        return StpUtil.getTokenInfo();
    }

    @RestPack
    @ROReject
    @KycReject
    @SaCheckPermission("user:create")
    @PostMapping("register")
    @SaCheckLogin
    public boolean register(@CurrentClientIp String clientIp, @RequestBody @Validated UserRegisterDto userRegisterDto) {
        if (!Validator.isEmail(userRegisterDto.getEmail())) {
            throw new ProgramException("邮箱格式不正确");
        }
        RLock lock = redissonClient.getLock("client:create:count:" + UserUtil.getId());
        try {
            if (!lock.tryLock()) {
                throw new ProgramException("提交过于频繁，请稍后再试");
            }
            String forbidRegisterEmailSuffix = clientParamService.getValueByCodeAndUserIdOrDefaultValue("FORBID_REGISTER_EMAIL_SUFFIX", null, "['@qq.com','@163.com']");
            Long clientCreateSubAccountMaxCount =
                    Long.valueOf(clientParamService.getValueByCodeAndUserIdOrDefaultValue("CLIENT_CREATE_SUB_ACCOUNT_MAX_COUNT", UserUtil.getId(), "5"));
            if (userService.countByParentId(UserUtil.getId()) >= clientCreateSubAccountMaxCount) {
                throw new ProgramException("当前用户创建子账户数量已达上限，请联系管理员");
            }
            List<String> list = JSONUtil.toList(forbidRegisterEmailSuffix, String.class);
            if (list.stream().anyMatch(it -> userRegisterDto.getEmail().contains(it))) {
                throw new ProgramException("当前邮箱后缀不允许注册");
            }
            if (userService.existsByAccount(userRegisterDto.getEmail())) {
                throw new ProgramException("邮箱已被注册");
            }
            if (!RedisCustomizeUtils.hasKey("create:" + userRegisterDto.getEmail().toLowerCase())) {
                throw new ProgramException("当前验证码已使用或已过期");
            }
            String code = RedisCustomizeUtils.get("create:" + userRegisterDto.getEmail().toLowerCase());
            if (!code.equals(userRegisterDto.getEmailCode())) {
                throw new ProgramException("验证码不正确");
            }
            User user = userService.init(userRegisterDto, UserUtil.getId());
            RedisCustomizeUtils.removeKey("create:" + userRegisterDto.getEmail().toLowerCase());
            roleService.init(userRegisterDto.getRoleId(), user.getId());
            //初始化钱包
            walletService.init(currencyService.list(), user.getId());
            userCardWalletService.init(cardService.list(), user.getId(), UserUtil.getId());
            comptrollerService.register(clientIp, user.getId(), 0);
//            smsComponent.sendActiveEmail(user.getEmail(), user.getId());
            cardGroupService.initDefaultGroup(user.getId());
        } catch (Exception e) {
            logger.error("创建子账户异常，异常信息：{}", e.getMessage());
            if (e instanceof ProgramException) {
                throw e;
            }
            throw new ProgramException("提交失败,请稍后再试");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return true;
    }

    /**
     * 注册
     *
     * @param clientIp
     * @param externalUserRegisterDto
     * @return
     */
    @RestPack
    @PostMapping("externalRegister")
    public boolean externalRegister(@CurrentClientIp String clientIp, @RequestBody ExternalUserRegisterDto externalUserRegisterDto) {
        if (!Validator.isEmail(externalUserRegisterDto.getEmail())) {
            throw new ProgramException("邮箱格式不正确");
        }
        String forbidRegisterEmailSuffix = clientParamService.getValueByCodeAndUserIdOrDefaultValue("FORBID_REGISTER_EMAIL_SUFFIX", null, "['@qq.com','@163.com']");
        List<String> list = JSONUtil.toList(forbidRegisterEmailSuffix, String.class);
        if (list.stream().anyMatch(it -> externalUserRegisterDto.getEmail().contains(it))) {
            throw new ProgramException("当前邮箱后缀不允许注册");
        }
        Long registerId;
        String registerSource;
        if (externalUserRegisterDto.getInvitationCode().length() == 6) {
            if (!bossUserService.existByInvitationCode(externalUserRegisterDto.getInvitationCode())) {
                throw new ProgramException("无效邀请码");
            }
            registerId = bossUserService.getByInvitationCode(externalUserRegisterDto.getInvitationCode());
            registerSource = "system";
        } else if (externalUserRegisterDto.getInvitationCode().length() == 8) {
            if (!userService.existsByInvitationCode(externalUserRegisterDto.getInvitationCode())) {
                throw new ProgramException("无效邀请码");
            }
            registerId = userService.getIdByInvitationCode(externalUserRegisterDto.getInvitationCode());
            registerSource = "client";
        } else {
            throw new ProgramException("无效邀请码");
        }
        externalUserRegisterDto.setRegisterId(registerId);
        externalUserRegisterDto.setRegisterSource(registerSource);
        if (!RedisCustomizeUtils.hasKey("register:" + externalUserRegisterDto.getEmail().toLowerCase())) {
            throw new ProgramException("当前验证码已使用或已过期");
        }
        String code = RedisCustomizeUtils.get("register:" + externalUserRegisterDto.getEmail().toLowerCase());
        if (!code.equals(externalUserRegisterDto.getEmailCode())) {
            throw new ProgramException("验证码不正确");
        }
        User user = userService.init(externalUserRegisterDto);
        RedisCustomizeUtils.removeKey("register:" + externalUserRegisterDto.getEmail().toLowerCase());
        //初始化钱包
        walletService.init(currencyService.list(), user.getId());
        Role clientInitRole = roleService.getClientInitRole();
        List<ClientPermission> clientPermissionList = clientPermissionService.listByRoleId(clientInitRole.getId());
        Role role = new Role();
        role.setUserId(user.getId());
        role.setName(clientInitRole.getName());
        role.setCode(clientInitRole.getCode());
        role.setHasAllData(clientInitRole.getHasAllData());
        role.setHasClientUpdate(clientInitRole.getHasClientUpdate());
        role.setHasClientIntoRole(false);
        role.setStatus(0);
        roleService.save(role);
        Set<Long> tempList = new HashSet<>();
        for (ClientPermission clientPermission : clientPermissionList) {
            if (clientPermission.getHasParent()) {
                tempList.addAll(clientPermissionService.listIdsByParentId(clientPermission.getId()));
            } else {
                tempList.add(clientPermission.getId());
                tempList.add(clientPermission.getParentId());
            }
        }
        List<ClientRolePermission> clientRolePermissions = tempList.stream().map(it -> {
            ClientRolePermission clientRolePermission = new ClientRolePermission();
            clientRolePermission.setRoleId(role.getId());
            clientRolePermission.setPermissionId(it);
            return clientRolePermission;
        }).toList();
        clientRolePermissionService.saveBatch(clientRolePermissions);
        roleService.init(role.getId(), user.getId());
        userCardWalletService.init(cardService.list(), user.getId());
        comptrollerService.register(clientIp, user.getId(), 0);
        cardGroupService.initDefaultGroup(user.getId());
        return true;
    }

    @RestPack
    @GetMapping("paging")
    @SaCheckPermission("user:view")
    @SaCheckLogin
    public Page<UserVo> paging(
            Page<User> pageable,
            Long targetUserId,
            Integer status,
            String remark
    ) {
        User user = UserUtil.get();
        Long parentId = user.getParentId();
        Role role = UserUtil.getRole();
        Page<User> paging = userService.paging(
                pageable,
                role.getHasAllData() && parentId != null && parentId != 0 ? parentId : user.getId(),
                role,
                targetUserId,
                status,
                remark);
        return paging.map(it -> {
            Role sourceRole = roleService.getRole(it.getId());
            String dealerAccount = userService.getAccountById(it.getBeLongToDealer());
            Long shareAccountCount = shareAccountService.countByUserId(it.getId());
            it.setDealerAccount(dealerAccount);
            List<Wallet> wallets = walletService.listByUserId(it.getId());
            return new UserVo(it, sourceRole, wallets,shareAccountCount);
        });
    }

//    @GetMapping("derive")
//    @SaCheckPermission("user:derive")
//    @SaCheckLogin
//    public void derive(
//            @CurrentROStatus Boolean roStatus,
//            Long targetUserId,
//            Integer status,
//            String remark,
//            HttpServletResponse response
//    ) {
//        User user = UserUtil.get();
//        Long parentId = user.getParentId();
//        Role role = UserUtil.getRole();
//        Long pages = 1L;
//        Page<User> pageable;
//        SXSSFWorkbook sxssfWorkbook = FileUtil.createUserTitle(10000);
//        Page<User> paging;
//        do {
//            pageable = new Page<>(pages++, 20);
//            paging = userService.paging(pageable, role.getHasAllData() && parentId != 0 ? parentId : user.getId(), role, targetUserId, status, remark);
//            List<UserFilePojo> list = paging.getRecords().stream().map(it -> {
//                String dealerAccount = userService.getAccountById(it.getBeLongToDealer());
//                List<WalletFilePojo> wallets = walletService.listByUserId(it.getId()).stream().map(wallet -> new WalletFilePojo(wallet.getCurrencyCode(), wallet.getUsableQuota(), wallet.getRechargeQuota(), wallet.getExchangeRate())).collect(Collectors.toList());
//                return new UserFilePojo(it.getAccount(), dealerAccount, role.getName(), it.getEmail(), 0, DateUtil.format(it.getCreateDate()), wallets);
//            }).collect(Collectors.toList());
//            FileUtil.deriveUserData(sxssfWorkbook, list, paging.getPageNumber(), paging.getPageSize());
//        } while (!(pages).equals(paging.getTotalPage() + 1));
//        String fileName = System.currentTimeMillis() + "用户详情.xlsx";
//        FileUtil.downXlsxFile(response, sxssfWorkbook, fileName);
//    }

    @RestPack
    @GetMapping("listDealerAccount")
    @SaCheckPermission("user:view:dealer")
    @SaCheckLogin
    public List<UserParamVo> listByAccount(
    ) {
        return userService.listDealer().stream().map(UserParamVo::new).collect(Collectors.toList());
    }

    @RestPack
    @GetMapping("listByEmail")
    @SaCheckPermission("user:search")
    @SaCheckLogin
    public List<UserParamVo> listByAccount(
            String email
    ) {
        if (RegexUtil.checkStringIsNull(email)) {
            throw new ProgramException("邮箱不能为空");
        }
        User user = UserUtil.get();
        Role role = UserUtil.getRole();
        Long id;
        if (role.getHasAllData()) {
            id = UserUtil.hasParent() ? user.getId() : user.getParentId();
        } else {
            id = user.getId();
        }
        return userService.listByEmail(email, id, role).stream().map(UserParamVo::new).collect(Collectors.toList());
    }

    @RestPack
    @ROReject
    @KycReject
    @SaCheckPermission("user:status:edit")
    @PostMapping("updateStatus")
    @SaCheckLogin
    public Boolean updateStatus(
            @CurrentClientIp String clientIp,
            @RequestBody UpdateUserStatusDto updateUserStatusDto) {
        if (RegexUtil.checkObjectIsNull(updateUserStatusDto.getTargetId())) {
            throw new ProgramException("操作用户不能为空");
        }
        if (RegexUtil.checkObjectIsNull(updateUserStatusDto.getStatus())) {
            throw new ProgramException("操作状态不能为空");
        }
        User user = userService.getById(updateUserStatusDto.getTargetId());
        if (Objects.equals(user.getId(), UserUtil.getId())) {
            throw new ProgramException("无法操作自己的状态");
        }
        if (user.getStatus().equals(updateUserStatusDto.getStatus())) {
            throw new ProgramException("状态一致无需修改");
        }
        // 0 可用 1 禁用  2  管理员禁用
        if (updateUserStatusDto.getStatus().equals(0)) {
            comptrollerService.unBlock(clientIp, user.getId(), 0);
        } else {
            comptrollerService.block(clientIp, user.getId(), 0);
        }

        boolean updated = userService.updateStatusById(updateUserStatusDto.getTargetId(), updateUserStatusDto.getStatus());
        StpUtil.logout(user.getId());
        return updated;
    }

    @RestPack
    @ROReject
    @KycReject
    @PostMapping("updatePayPassword")
    @SaCheckSafe("UPDATE_USER_PAY_PASSWORD")
    @SaCheckLogin
    public Boolean updatePayPassword(
            @CurrentClientIp String clientIp,
            @RequestBody UpdateUserPayPasswordDto updateUserPayPasswordDto) {
        StpUtil.closeSafe("UPDATE_USER_PAY_PASSWORD");
        if (RegexUtil.checkObjectIsNull(updateUserPayPasswordDto.getPayPassword())) {
            throw new ProgramException("支付密码不能为空");
        }
        User user = userService.getById(UserUtil.getId());
        if (StringUtils.isNotBlank(user.getPayPassword())) {
            if (!EncryptUtil.encryptValue(updateUserPayPasswordDto.getSourcePayPassword(), user.getSalt()).equals(user.getPayPassword())) {
                throw new ProgramException("原始密码不相同");
            }
            if (user.getPayPassword().equals(EncryptUtil.encryptValue(updateUserPayPasswordDto.getPayPassword(), user.getSalt()))) {
                throw new ProgramException("前后密码一致无需修改");
            }
        }
        boolean flag = userService.updatePayPasswordById(user.getId(), EncryptUtil.encryptValue(updateUserPayPasswordDto.getPayPassword(), user.getSalt()));
        comptrollerService.updatePayPassword(clientIp, user.getId(), flag ? 0 : 1);
        UserUtil.set(userService.getById(user.getId()), roleService.getRole(user.getId()));
        return flag;
    }

    @RestPack
    @ROReject
    @KycReject
    @PostMapping("updatePassword")
    @SaCheckLogin
    @SaCheckSafe("UPDATE_USER_PASSWORD")
    public Boolean updatePassword(
            @CurrentClientIp String clientIp,
            @RequestBody @Validated UpdateUserPasswordDto updateUserPasswordDto) {
        StpUtil.closeSafe("UPDATE_USER_PASSWORD");
        if (RegexUtil.checkObjectIsNull(updateUserPasswordDto.getPassword())) {
            throw new ProgramException("密码不能为空");
        }
        User user = userService.getById(UserUtil.getId());
        System.out.println("原始密码：" + updateUserPasswordDto.getSourcePassword());
        System.out.println("用户盐值：" + user.getSalt());
        System.out.println("加密后的原始密码：" + EncryptUtil.encryptValue(updateUserPasswordDto.getSourcePassword(), user.getSalt()));
        System.out.println("用户密码：" + user.getPassword());
        if (!EncryptUtil.encryptValue(updateUserPasswordDto.getSourcePassword(), user.getSalt()).equals(user.getPassword())) {
            throw new ProgramException("原始密码不相同");
        }
        if (user.getPassword().equals(EncryptUtil.encryptValue(updateUserPasswordDto.getPassword(), user.getSalt()))) {
            throw new ProgramException("前后密码一致无需修改");
        }
        boolean flag = userService.updatePasswordById(user.getId(), EncryptUtil.encryptValue(updateUserPasswordDto.getPassword(), user.getSalt()));
        comptrollerService.updatePassword(clientIp, user.getId(), flag ? 0 : 1);
        return flag;
    }

    @RestPack
    @ROReject
    @KycReject
    @SaCheckPermission("user:remark:edit")
    @PostMapping("updateRemark")
    @SaCheckLogin
    public Boolean updateRemark(
            @RequestBody UpdateUserRemarkDto updateUserRemarkDto) {
        if (RegexUtil.checkObjectIsNull(updateUserRemarkDto.getTargetId())) {
            throw new ProgramException("操作用户不能为空");
        }
        User user = userService.getById(updateUserRemarkDto.getTargetId());
        if (user.getPayPassword().equals(updateUserRemarkDto.getRemark())) {
            throw new ProgramException("前后备注无需修改");
        }
        return userService.updateRemarkById(updateUserRemarkDto.getTargetId(), updateUserRemarkDto.getRemark());
    }

    @RestPack
    @ROReject
    @PostMapping("active")
    public Boolean active(
            @CurrentClientIp String clientIp,
            @RequestBody @Validated EmailActiveDto emailActiveDto) {
        if (RegexUtil.checkObjectIsNull(emailActiveDto.getCode())) {
            throw new ProgramException("激活码不能为空");
        }
        String code = emailActiveDto.getCode();
        if (!RedisCustomizeUtils.hasKey("active:" + code)) {
            throw new ProgramException("激活码已使用或已过期");
        }
        String sourceUserId = RedisCustomizeUtils.get("active:" + code);
        RedisCustomizeUtils.removeKey("active:" + code);
        Long userId = Long.valueOf(sourceUserId);
        return userService.active(userId);
    }

    @RestPack
    @ROReject
    @PostMapping("emailUpdatePassword")
    public Boolean emailUpdatePassword(
            @CurrentClientIp String clientIp,
            @RequestBody EmailUpdatePasswordDto emailUpdatePasswordDto) {
        if (RegexUtil.checkObjectIsNull(emailUpdatePasswordDto.getCode())) {
            throw new ProgramException("验证码码不能为空");
        }
        if (RegexUtil.checkObjectIsNull(emailUpdatePasswordDto.getPassword())) {
            throw new ProgramException("密码不能为空");
        }
        String code = emailUpdatePasswordDto.getCode();
        String key = code.replace("-", ":");
        if (!RedisCustomizeUtils.hasKey(key)) {
            throw new ProgramException("当前链接已使用或已过期");
        }
        String sourceUserId = RedisCustomizeUtils.get(key);
        RedisCustomizeUtils.removeKey(key);
        Long userId = Long.valueOf(sourceUserId);
        User user = userService.getById(userId);
        if (user.getPassword().equals(EncryptUtil.encryptValue(emailUpdatePasswordDto.getPassword(), user.getSalt()))) {
            throw new ProgramException("前后密码一致无需修改");
        }
        return userService.updatePasswordById(userId, EncryptUtil.encryptValue(emailUpdatePasswordDto.getPassword(), user.getSalt()));
    }

    @PostMapping("checkUserSafe")
    @RestPack
    @SaCheckLogin
    public Boolean checkUserSafe(
            @RequestBody @Validated CheckUserSafeDto checkUserSafeDto,
            @CurrentClientIp String clientIp
    ) {
        Long id = UserUtil.getId();
        User user = userService.getById(id);
        switch (checkUserSafeDto.getType()) {
            case "payPassword": {
                if (!user.getPayPassword().equals(EncryptUtil.encryptValue(checkUserSafeDto.getValue(), user.getSalt()))) {
                    Long increase = RedisCustomizeUtils.increase("verify:error:count:" + user.getId(), 1L, 60L);
                    if (increase >= 5) {
                        userService.updateStatusById(user.getId(), 2);
                        comptrollerService.block(clientIp, user.getId(), 0);
                        throw new ProgramException("登录失败次数过多，账户已被锁定");
                    }
                    throw new ProgramException("支付密码不正确");
                }
                break;
            }
            default: {
                if (!SaTotpUtil.validateTOTP(SaSecureUtil.aesDecrypt(user.getSalt(), user.getGoogleSecret()), checkUserSafeDto.getValue(), 1) && activeProfile.equals("prod")) {
                    Long increase = RedisCustomizeUtils.increase("verify:error:count:" + user.getId(), 1L, 60L);
                    if (increase >= 5) {
                        userService.updateStatusById(user.getId(), 2);
                        comptrollerService.block(clientIp, user.getId(), 0);
                        throw new ProgramException("登录失败次数过多，账户已被锁定");
                    }
                    throw new ProgramException("谷歌验证码不正确");
                }
            }
        }
        RedisCustomizeUtils.removeKey("verify:error:count:" + user.getId());
        String verifyUserSafe = clientParamService.getValueByCodeAndUserId("VERIFY_USER_SAFE", user.getId());
        if (RegexUtil.checkStringIsNull(verifyUserSafe)) {
            throw new ProgramException("当前用户未设置安全验证方式");
        }
        long verifyUserSafeTime = Long.parseLong(verifyUserSafe) * 60L;
        StpUtil.openSafe(checkUserSafeDto.getService(), verifyUserSafeTime);
        return true;
    }

    @RestPack
    @PostMapping("logout")
    @SaCheckLogin
    public boolean logout() {
        UserUtil.logout();
        return true;
    }
}


